/**
 * 问题：
 * 提供了一个数组结构的 data，要求实现一个 query 方法，返回一个新的数组, 
 * query 方法内部有 过滤、排序、分组 等操作，并且支持链式调用,
 * 调用最终的 execute 方法返回结果：
 * 
 * 示例：
 * const result = query(list)
 * .where(item => item.age > 18)
 * .sortBy('id')
 * .groupBy('name')
 * .execute();
 * console.log(result);
*/

const data = [
  { name: 'foo', age: 16, city: 'shanghai' },
  { name: 'bar', age: 24, city: 'hangzhou' },
  { name: 'fiz', age: 22, city: 'shanghai' },
  { name: 'baz', age: 19, city: 'hangzhou' }
]

/**
 * 1.使用原型链实现
 */ 
function query(arr) {
  let list = arr;
  // 构造函数
  function MyArray() {}
  // 校验数组
  if (!Array.isArray(list)) {
    return console.error('list must be an array')
  }
  // where方法 返回原型
  MyArray.prototype.where = function(cb) {
    list = list.filter(cb)
    return { ...MyArray.prototype }
  }
  // orderBy方法 返回原型
  MyArray.prototype.orderBy = function(key) {
    list = list.sort((a, b) => a[key] - b[key])
    return { ...MyArray.prototype }
  }
  // groupBy方法 返回原型
  MyArray.prototype.groupBy = function(key) {
    let group = list.reduce((prev,item) => {
      return { ...prev, [item[key]]: [...(prev[item[key]] || []), item] }
    }, {})
    list = [].concat(...Object.values(group))
    return { ...MyArray.prototype }
  }
  // groupBy方法 返回list
  MyArray.prototype.execute = function() {
    return list
  }
  // query方法 返回原型
  return { ...MyArray.prototype }
}

// 测试1
let res = query(data)
    .where((item) => item.age > 18)
    .orderBy("age")
    .groupBy("city")
    .execute();
console.log(res);
// [
//   { "name": "baz", "age": 19, "city": "hangzhou" },
//   { "name": "bar", "age": 24, "city": "hangzhou" },
//   { "name": "fiz", "age": 22, "city": "shanghai" }
// ]




/**
 *  2.使用构造函数实现
*/
function query2(arr) {
  // 构造函数
  function Query(list) {
    this.list = list
    // 校验数组
    if (!Array.isArray(list)) {
      return console.error('list must be an array')
    }
    // where方法 返回实例
    this.where = function(cb) {
      this.list = this.list.filter(cb)
      return this
    }
    // orderBy方法 返回实例
    this.orderBy = function(key) {
      this.list = this.list.sort((a, b) => a[key] - b[key])
      return this
    }
    // groupBy方法 返回实例
    this.groupBy = function(key) {
      let group = this.list.reduce((prev,item) => {
        return { ...prev, [item[key]]: [...(prev[item[key]] || []), item] }
      }, {})
      this.list = [].concat(...Object.values(group))
      return this
    }
    // groupBy方法 返回list
    this.execute = function() {
      return this.list
    }
  }

  return new Query(arr)
}

// 测试2
let res2 = query2(data)
    .where((item) => item.age > 18)
    .orderBy("age")
    .groupBy("city")
    .execute();
console.log(res2);
// [
//   { "name": "baz", "age": 19, "city": "hangzhou" },
//   { "name": "bar", "age": 24, "city": "hangzhou" },
//   { "name": "fiz", "age": 22, "city": "shanghai" }
// ]
